
import * as THREE from 'three';
import threeToMap from 'tools/fns/threeToMap';
import State from '../State';
import Element, { GLTF, IObject3DMap } from '../State/Element';

export default async function saveData(s: State) {
    const items = [...s.elementMap.values()].map(e => {
        return {
            uuid: e.uuid,
            name: e.name,
            type: e.type,
            data: e.data,
            object3D: getObject3D(s, e),
        }
    })

    const floor = {
        width: s.floor?.geometry.parameters.width,
        height: s.floor?.geometry.parameters.height,
        color: (s.floor?.material as THREE.MeshPhongMaterial).color.getHex(),
    }

    const camera = {
        position: {
            x: s.camera?.position.x,
            y: s.camera?.position.y,
            z: s.camera?.position.z
        },
        target: {
            x: s.controls?.target.x,
            y: s.controls?.target.y,
            z: s.controls?.target.z,
        }
    }

    return {
        camera,
        floor,
        items,
    }
}

/** 3D数据 */
function getObject3D(s: State, e: Element<keyof IObject3DMap, any>) {
    return {
        ...base(e),
        ...gltf(e),
        ...light(e),
        ...ambientLight(e),
        ...spotLight(s, e),
        ...directionalLight(s, e),
        ...pointLight(e),
        ...rectAreaLight(e),
        ...shadow(e),
    }
}

/** 基础 */
function base(e: Element<keyof IObject3DMap, any>) {
    const object3D = e.getObject3D();
    return {
        visible: object3D?.visible,
        castShadow: object3D?.castShadow,
        receiveShadow: object3D?.receiveShadow,
        position: { x: object3D?.position.x, y: object3D?.position.y, z: object3D?.position.z },
        rotation: { x: object3D?.rotation.x, y: object3D?.rotation.y, z: object3D?.rotation.z },
        scale: { x: object3D?.scale.x, y: object3D?.scale.y, z: object3D?.scale.z },
    }
}

/** GLTF */
function gltf(e: Element<keyof IObject3DMap, any>) {
    if (e.type !== 'gltf') return {};
    const gltf = e.object3D as GLTF;
    
    return {
        path: gltf.parser.options.path,
        animationName: gltf.animationName,
    }
}

/** 灯光 */
function light(e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.Light === false) return {};
    const light = e.object3D as THREE.Light;
    return {
        color: light.color.getHex(),
        intensity: light.intensity,
    }
}

/** 环境光 */
function ambientLight(e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.AmbientLight === false) return {};
    return {
        lightType: 'AmbientLight',
    }
}

/** 聚光灯 */
function spotLight(s: State, e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.SpotLight === false) return {};
    const spotLight = e.object3D as THREE.SpotLight;
    return {
        lightType: 'SpotLight',
        angle: spotLight.angle,
        decay: spotLight.decay,
        distance: spotLight.distance,
        power: spotLight.power,
        target: threeToMap(s.elementMap, spotLight.target.uuid),
    }
}

/** 平行光 */
function directionalLight(s: State, e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.DirectionalLight === false) return {};
    const directionalLight = e.object3D as THREE.DirectionalLight;
    return {
        lightType: 'DirectionalLight',
        target: threeToMap(s.elementMap, directionalLight.target.uuid),
    }
}

/** 点光源 */
function pointLight(e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.PointLight === false) return {};
    const pointLight = e.object3D as THREE.PointLight;
    return {
        lightType: 'PointLight',
        power: pointLight.power,
    }
}

/** 平面光 */
function rectAreaLight(e: Element<keyof IObject3DMap, any>) {
    if (e.object3D instanceof THREE.RectAreaLight === false) return {};
    const rectAreaLight = e.object3D as THREE.RectAreaLight;
    return {
        lightType: 'RectAreaLight',
        width: rectAreaLight.width,
        height: rectAreaLight.height,
    }
}

/** 阴影参数 */
function shadow(e: Element<keyof IObject3DMap, any>) {
    try {
        const light = e.object3D as any;
        return {
            bias: light.shadow.bias,
        }
    } catch (error) {
        return {}
    }
}
